package game.Map;

import game.Configuration;
import game.Utils.GraphicsUtils;
import game.Vector3;
import javafx.scene.image.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;

import java.awt.Point;
import java.nio.IntBuffer;


public class MapChunk {
    protected final Map chunkMap;
    protected final Point chunkPosition; // In tile units
    protected final int chunkSize;

    // Render image of chunk
    protected WritableImage chunkImage;

    public MapChunk(Point position, int size, Map map){
        this.chunkMap = map;
        this.chunkPosition = position;
        this.chunkSize = size;

        int tilePixels = (int)(map.getTileImage(chunkPosition.x,chunkPosition.y).getWidth());
        int imageWidth , imageHeight;
        imageHeight = imageWidth = size * tilePixels;

        this.chunkImage = new WritableImage(imageWidth, imageHeight);
        this.updateChunk();
    }

    /**
     * Renders the actual chunk image into chunkImage
     */
    protected void updateChunk(){
        double tilePixels = chunkMap.getTileImage(chunkPosition.x,chunkPosition.y).getWidth();
        Image background = chunkMap.getBackgroundImage();
        for(int i = 0; i < chunkSize; i++){
            for(int j = 0; j < chunkSize; j++){
                Image tileImg = chunkMap.getTileImage(chunkPosition.x + i, chunkPosition.y + j);
                if(tileImg == null)
                    continue;

                // Draw tile image
                PixelReader pixelReader = tileImg.getPixelReader();
                int sourceWidth = (int) tileImg.getWidth();
                int sourceHeight = (int) tileImg.getHeight();
                int[] buffer = new int[sourceWidth * sourceHeight];

                PixelWriter pixelWriter = chunkImage.getPixelWriter();
                WritablePixelFormat<IntBuffer> format = WritablePixelFormat.getIntArgbInstance();
                pixelReader.getPixels(0, 0, sourceWidth, sourceHeight, format, buffer, 0, sourceWidth);
                if(background!=null) {
                    //TODO PAint background
                    pixelWriter.setPixels((int) (i * tilePixels), (int) (j * tilePixels), sourceWidth, sourceHeight, format, buffer, 0, sourceWidth);
                }else {
                    pixelWriter.setPixels((int) (i * tilePixels), (int) (j * tilePixels), sourceWidth, sourceHeight, format, buffer, 0, sourceWidth);
                }

                // Draw shadows on tile
                if(Configuration.isShadows()) {
                    if (!chunkMap.getTile(chunkPosition.x + i, chunkPosition.y + j).isWall()) {
                        Tile top = chunkMap.getTile(chunkPosition.x + i, chunkPosition.y + j - 1);
                        Tile topRight = chunkMap.getTile(chunkPosition.x + i + 1, chunkPosition.y + j - 1);
                        Tile right = chunkMap.getTile(chunkPosition.x + i + 1, chunkPosition.y + j);
                        Tile bottomRight = chunkMap.getTile(chunkPosition.x + i + 1, chunkPosition.y + j + 1);
                        Tile bottom = chunkMap.getTile(chunkPosition.x + i, chunkPosition.y + j + 1);
                        Tile bottomLeft = chunkMap.getTile(chunkPosition.x + i - 1, chunkPosition.y + j + 1);
                        Tile left = chunkMap.getTile(chunkPosition.x + i - 1, chunkPosition.y + j);
                        Tile topLeft = chunkMap.getTile(chunkPosition.x + i - 1, chunkPosition.y + j - 1);


                        double shadowSize = tilePixels / 2;
                        double shadowIntensity = 0.5;
                        pixelReader = chunkImage.getPixelReader();

                        // Top shadow
                        if (top != null && !top.isWalkable()) {
                            for (int y = 0; y < shadowSize; y++) {
                                double alpha = (double) (shadowSize - y) / shadowSize * shadowIntensity;
                                Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);
                                //gd.setColor(c);
                                GraphicsUtils.multiplyRect(pixelReader, pixelWriter,
                                        (int) (i * tilePixels), (int) (j * tilePixels + y), (int) tilePixels, 1, c);

                                //gd.fillRect( i*tilePixels, j*tilePixels+y, tilePixels, 1);
                            }
                        }

                        // Bottom shadow
                        if (bottom != null && !bottom.isWalkable()) {
                            for (int y = 0; y < shadowSize; y++) {
                                double alpha = (double) (shadowSize - y) / shadowSize * shadowIntensity;

                                Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                GraphicsUtils.multiplyRect(pixelReader, pixelWriter,
                                        (int) (i * tilePixels), (int) (((j + 1) * tilePixels) - 1 - y), (int) tilePixels, 1, c);
                            }
                        }


                        // Left shadow
                        if (left != null && !left.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                double alpha = (double) (shadowSize - x) / shadowSize * shadowIntensity;

                                Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);
                                GraphicsUtils.multiplyRect(pixelReader, pixelWriter,
                                        (int) (i * tilePixels) + x, (int) (j * tilePixels), 1, (int) tilePixels, c);
                            }
                        }


                        // Right shadow
                        if (right != null && !right.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                double alpha = (double) (shadowSize - x) / shadowSize * shadowIntensity;
                                Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                GraphicsUtils.multiplyRect(pixelReader, pixelWriter,
                                        (int) ((i + 1) * tilePixels - 1) - x, (int) (j * tilePixels), 1, (int) tilePixels, c);
                            }
                        }


                        // Top-right shadow
                        if ((top == null || top.isWalkable()) && (right == null || right.isWalkable()) &&
                                topRight != null && !topRight.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                for (int y = 0; y < shadowSize; y++) {
                                    double alphah = (double) (shadowSize - x) / shadowSize;
                                    double alphav = (double) (shadowSize - y) / shadowSize;
                                    double alpha = alphah * alphav * shadowIntensity;
                                    Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                    GraphicsUtils.multiplyColor(pixelReader, pixelWriter,
                                            (int) ((i + 1) * tilePixels - 1) - x, (int) (j * tilePixels) + y, c);
                                }
                            }
                        }

                        // Bottom-right shadow
                        if ((bottom == null || bottom.isWalkable()) && (right == null || right.isWalkable()) &&
                                bottomRight != null && !bottomRight.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                for (int y = 0; y < shadowSize; y++) {
                                    double alphah = (double) (shadowSize - x) / shadowSize;
                                    double alphav = (double) (shadowSize - y) / shadowSize;
                                    double alpha = alphah * alphav * shadowIntensity;
                                    Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                    GraphicsUtils.multiplyColor(pixelReader, pixelWriter,
                                            (int) ((i + 1) * tilePixels - 1) - x, (int) ((j + 1) * tilePixels) - 1 - y, c);
                                }
                            }
                        }

                        // Bottom-left shadow
                        if ((bottom == null || bottom.isWalkable()) && (left == null || left.isWalkable()) &&
                                bottomLeft != null && !bottomLeft.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                for (int y = 0; y < shadowSize; y++) {
                                    double alphah = (double) (shadowSize - x) / shadowSize;
                                    double alphav = (double) (shadowSize - y) / shadowSize;
                                    double alpha = alphah * alphav * shadowIntensity;
                                    Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                    GraphicsUtils.multiplyColor(pixelReader, pixelWriter,
                                            (int) (i * tilePixels) + x, (int) ((j + 1) * tilePixels) - 1 - y, c);
                                }
                            }
                        }

                        // Top-left shadow
                        if ((top == null || top.isWalkable()) && (left == null || left.isWalkable()) &&
                                topLeft != null && !topLeft.isWalkable()) {
                            for (int x = 0; x < shadowSize; x++) {
                                for (int y = 0; y < shadowSize; y++) {
                                    double alphah = (double) (shadowSize - x) / shadowSize;
                                    double alphav = (double) (shadowSize - y) / shadowSize;
                                    double alpha = alphah * alphav * shadowIntensity;
                                    Color c = new Color(1.0 - alpha, 1.0 - alpha, 1.0 - alpha, 1.0);

                                    GraphicsUtils.multiplyColor(pixelReader, pixelWriter,
                                            (int) (i * tilePixels) + x, (int) (j * tilePixels) + y, c);
                                }
                            }
                        }
                    }
                }
            }
        }
    }


    public Image getChunkImage(){
        return this.chunkImage;
    }

    public Vector3 getPosition(){
        return chunkMap.tileToGlobalPos(chunkPosition);
    }

    public int getSize(){
        return this.chunkSize;
    }
}
